/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.sunspotworld.manager;

import java.io.File;
import org.sunspotworld.executor.CommandExecutor;
import org.sunspotworld.executor.DataExecutor;
import org.sunspotworld.executor.FileExecutor;

/**
 * This class provides all the DTN related operations
 * @author Laili Aidi - aidi@kth.se
 * Condroid Summer CSD 2011 - KTH
 */
public class DTNManager {

    private final String dtncpDTN = "dtncp";
    private final String dtncpdDTN = "dtncpd";
    private String obStrIncomingPath;
    private String obStrOutgoingPath;
    /* The Receiver DTN Addr of the intended bundle*/
    private String obStrReceiver;
    /* The dtnd path of the DTN node */
    private String obStrDtndPath;
    /* The dtnd conf of the DTN node */
    private String obStrDtnConfPath;
    private CommandExecutor obCommandExecutor;
    private FileExecutor obFileExecutor;
    private DataExecutor obDataExecutor;

    /**
     * This the constructore of DTNManager class, used in Core-Engine Sender
     * @param obStrReceiver
     * @param obDataExecutor
     * @param obStrOutgoingPath
     * @param obStrDtndPath
     * @param obStrDtnConfPath
     */
    public DTNManager(String obStrReceiver, DataExecutor obDataExecutor, String obStrOutgoingPath, String obStrDtndPath, String obStrDtnConfPath) {
        //Get the param
        this.obStrReceiver = obStrReceiver;
        this.obStrOutgoingPath = obStrOutgoingPath;
        this.obDataExecutor = obDataExecutor;
        this.obStrDtndPath = obStrDtndPath;
        this.obStrDtnConfPath = obStrDtnConfPath;
        //Initiate the class that we need
        this.obCommandExecutor = new CommandExecutor();
        this.obFileExecutor = new FileExecutor(obStrOutgoingPath);
    }

    /**
     * This the constructore of DTNManager class, used in Core-Engine Receiver
     * @param obDataExecutor
     * @param obStrIncomingPath
     * @param obStrDtndPath
     * @param obStrDtnConfPath
     */
    public DTNManager(DataExecutor obDataExecutor, String obStrIncomingPath, String obStrDtndPath, String obStrDtnConfPath) {
        //Get the param
        this.obStrIncomingPath = obStrIncomingPath;
        this.obDataExecutor = obDataExecutor;
        this.obStrDtndPath = obStrDtndPath;
        this.obStrDtnConfPath = obStrDtnConfPath;
        //Initiate the class that we need
        this.obCommandExecutor = new CommandExecutor();
        this.obFileExecutor = new FileExecutor(obStrIncomingPath);
    }

    /**
     * This method is used to handle data injection from MySQL database to the file 
     * It compress the file contain the data as tar.gz then send it as the DTN bundle 
     * @param obStrDTNFileName
     * @return isSuccess
     */
    public boolean distributeData(String obStrDTNFileName) {
        //Compress the filetar first
        if (obFileExecutor.compresFile(obStrDTNFileName)) {
            //Then send it as the bundle
            File filetar = new File(obStrOutgoingPath + "/" + obStrDTNFileName + ".tar.gz");
            boolean exists = filetar.exists();
            //System.out.println("Check if filetar exist " + (obStrOutgoingPath + "/" + obStrDTNFileName) + ".tar.gz =" + exists);
            if (!exists) {
                // It returns false if File or directory does not exist
                // System.out.println("MESSAGE: File " + obStrDTNFileName + " is not exist");
            } else {
                System.out.println("MESSAGE: Sending bundle " + (obStrOutgoingPath + "/" + obStrDTNFileName + ".tar.gz"));
                // It returns true if File or directory exists
                boolean isSuccess = sendBundle(obStrOutgoingPath + "/" + obStrDTNFileName + ".tar.gz");
                //Even if can't send it as bundle now, keep delete it, we try in another round.
                if (!filetar.canWrite()) {
                    System.out.println("MESSAGE: File " + (obStrOutgoingPath + "/" + obStrDTNFileName + ".tar.gz") + " is protected, can't be deleted");
                } else {
                    System.out.println("MESSAGE: file " + (obStrOutgoingPath + "/" + obStrDTNFileName + ".tar.gz") + " was deleted");
                    filetar.delete();
                }
                return true;
            }
        }
        return false;
    }

    /**
     * This method is used in automatic distribution.
     * It creates the file contain the data first, then compress as tar.gz
     * then send it as the DTN bundle 
     * @param obStrDTNFileName
     * @param obStrText
     * @return isSuccess
     */
    public boolean distributeData(String obStrDTNFileName, String obStrText) {
        //write the data to the filetar
        if (obFileExecutor.writeFile(obStrDTNFileName, obStrText)) {
            if (distributeData(obStrDTNFileName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * This method is used to handle data injection from DTN bundle to MySQL database.
     * It receive the file tar.gz, extract it, read the content then inject the data to database
     * @param obStrReceiverName
     * @param obStrDTNFileName
     * @return
     */
    public boolean receiveData(String obStrReceiverName, String obStrDTNFileName) {
        //Uncompress the filetar first
        if (obFileExecutor.uncompressFile(obStrReceiverName, obStrDTNFileName)) {
            File file = new File(obStrIncomingPath + "/" + obStrDTNFileName);
            boolean exists = file.exists();
            System.out.println("Check if file exist " + (obStrIncomingPath + "/" + obStrDTNFileName) + " =" + exists);
            if (!exists) {
                // It returns false if File or directory does not exist
                //System.out.println("MESSAGE: File " + obStrIncomingPath + "/" + obStrDTNFileName + " is not exist");
            } else {
                //read the data on the filetar
                String obStrReceiveData = obFileExecutor.readFile(obStrDTNFileName);
                if (obStrReceiveData != null) {
                    System.out.println("MESSAGE: Receiving bundle " + (obStrIncomingPath + "/" + obStrDTNFileName));
                    String[] temp = obStrReceiveData.split("\n");
                    for (int i = 0; i < temp.length; i++) {
                        String[] temp2 = temp[i].split("\' ");
                        try {
                            /* The format in each line is
                             * 'GatewayAddr' 'GatewayName' 'GatewayLocation' 'SpotAddr' 'SpotLoc' 'timestap' 'light' 'temperature' 'batt'
                             * Thus we substring the ' character
                             */
                            String obStrGatewayAddr = temp2[0].substring(1, temp2[0].length());
                            String obStrGatewayName = temp2[1].substring(1, temp2[1].length());
                            String obStrGatewayLocation = temp2[2].substring(1, temp2[2].length());
                            String obStrSpotAddr = temp2[3].substring(1, temp2[3].length());
                            String obStrSpotLocation = temp2[4].substring(1, temp2[4].length());
                            String obTimeStamp = temp2[5].substring(1, temp2[5].length());
                            int obIntLight = Integer.parseInt(temp2[6].substring(1, temp2[6].length()));
                            double obDoubleTemp = Double.parseDouble(temp2[7].substring(1, temp2[7].length() - 1));
                            double obDoubleBatt = Double.parseDouble(temp2[8].substring(1, temp2[8].length() - 1));

                            //Set Flag as 1 so this engine will not send the data back to the DTN network
                            int obFlag = 1;
                            System.out.println("Receive Data " + obStrGatewayAddr + " " + obStrGatewayName + " " + obStrGatewayLocation + " " + obStrSpotAddr + " " + obStrSpotLocation + " " + obTimeStamp + " " + obDoubleTemp + " " + obDoubleBatt);

                            //Check if this data already exist or not
                            if (obDataExecutor.isSensingExist(obStrSpotAddr, obTimeStamp)) {
                                //Write this data to database
                                System.out.println("Data from Spot " + obStrSpotAddr + " with timestamp " + obTimeStamp + " is recorded");
                                obDataExecutor.writeSensingTable(obStrGatewayAddr, obStrGatewayName, obStrGatewayLocation, obStrSpotAddr, obStrSpotLocation, obTimeStamp, obIntLight, obDoubleTemp, obDoubleBatt, obFlag);
                            } else {
                                System.out.println("Data from Spot " + obStrSpotAddr + " with timestamp " + obTimeStamp + " has been already received before");
                            }
                        } catch (Exception e) {
                            System.out.println("Caught receiveData 1: " + e.getMessage());
                        }
                    }
                }
                //After read the data and/or write it to the database, delete it
                File filetar = new File(obStrIncomingPath + "/" + obStrReceiverName + "/" + obStrDTNFileName + ".tar.gz");
                if (filetar.exists() && filetar.canWrite()) {
                    filetar.delete();
                }
                //Even if can't send it as bundle now, keep delete it, we try in another round.
                if (!file.canWrite()) {
                    System.out.println("MESSAGE: File " + (obStrIncomingPath + "/" + obStrDTNFileName) + " is protected, can't be deleted");
                } else {
                    System.out.println("MESSAGE: file " + (obStrIncomingPath + "/" + obStrDTNFileName) + " was deleted");
                    file.delete();
                }
                return true;
            }
        }
        return false;
    }
    private final int WAIT_COMMAND = 2000; //WAIT FOR 2 SECONDS
    Thread DTNStarter, DTNSender, DTNReceiver;

    /**
     * This method is used to stop the DTN daemon
     */
    public void stopDTN() {

        try {
            while (DTNSender.isAlive()) {
                System.out.println("\nStoping DTN Send ... ");
                DTNSender.interrupt();
            }
            System.out.println("\nKILL DTN SENDER ... " + DTNSender.isAlive());
        } catch (Exception e) {
            System.err.println("ERROR in stopDTN1 : " + e.getMessage() + " - Can not stop the DTNSender");
        }

        try {
            while (DTNReceiver.isAlive()) {
                System.out.println("\nStoping DTN Receive ... ");
                DTNReceiver.interrupt();
                System.out.println("\nKILL DTN RECEIVER1 ... " + DTNReceiver.isAlive());
            }
        } catch (Exception e) {
            System.err.println("ERROR in stopDTN2 : " + e.getMessage() + " - Can not stop the DTNReceiver");
        }
    }

    /**
     * This method is used to start the DTN daemon and set the intended receiver
     * @param obStrReceiver 
     * @return isSuccess
     */
    public boolean startDTN(String obStrReceiver) {
        this.obStrReceiver = obStrReceiver;
        boolean isSuccess = startDTN();
        return isSuccess;
    }

    /**
     * This method is used to start the DTN daemon
     * @return isSuccess
     */
    public boolean startDTN() {
        //Start the DTN Daemon
        System.out.println("\nStarting DTN Daemon ... ");
        System.out.println("COMMAND\t" + obStrDtndPath + " -c " + obStrDtnConfPath + " -l info");
        Long start = System.currentTimeMillis();
        DTNStarter = new Thread() {

            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    obCommandExecutor.runCommand(obStrDtndPath + " -c " + obStrDtnConfPath + " -l info");
                }
            }
        };
        DTNStarter.start();

        while (true) {
            if (System.currentTimeMillis() - start > WAIT_COMMAND) {
                break;
            }
        }

        if (DTNStarter.isAlive()) {
            return true;
        }
        return false;
    }

    /**
     * This method is used to do DTN file send
     * @param obStrFileName
     * @return isSuccess
     */
    public boolean sendBundle(String obStrFileName) {
        //Start the DTN Daemon
        System.out.println("\nStarting DTN Sender ... ");
        final String obStrFileName_Tread = obStrFileName;
        System.out.println("COMMAND\t" + dtncpDTN + " " + obStrFileName + " " + obStrReceiver);
        Long start = System.currentTimeMillis();
        DTNSender = new Thread() {

            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    obCommandExecutor.runCommand(dtncpDTN + " " + obStrFileName_Tread + " " + obStrReceiver);
                }
            }
        };
        DTNSender.start();

        while (true) {
            if (System.currentTimeMillis() - start > WAIT_COMMAND) {
                break;
            }
        }

        if (DTNSender.isAlive()) {
            System.out.println("MESSAGE: DTN bundle sending was successful");
            return true;
        }
        System.out.println("ERROR: DTN bundle sending was fail");
        return false;
    }

    /**
     * This method is used to do DTN file receive
     * @return isSuccess
     */
    public boolean receiveBundle() {
        //Start the DTN Daemon
        System.out.println("\nStarting DTN Receiving ... ");
        System.out.println("COMMAND\t" + dtncpdDTN + " " + obStrIncomingPath);
        Long start = System.currentTimeMillis();
        DTNReceiver = new Thread() {

            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    obCommandExecutor.runCommand(dtncpdDTN + " " + obStrIncomingPath);
                }

            }
        };
        DTNReceiver.start();

        while (true) {
            if (System.currentTimeMillis() - start > WAIT_COMMAND) {
                break;
            }
        }

        if (DTNReceiver.isAlive()) {
            System.out.println("MESSAGE: DTN bundle receiving was successful");
            return true;
        }
        System.out.println("ERROR: DTN bundle receiving was fail");
        return false;
    }
}
